package com.best.oasis.settlement.util.orika;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dozer.classmap.ClassMap;
import org.dozer.classmap.ClassMappings;
import org.dozer.classmap.Configuration;
import org.dozer.classmap.MappingFileData;
import org.dozer.converters.CustomConverterDescription;
import org.dozer.fieldmap.FieldMap;
import org.dozer.loader.CustomMappingsLoader;
import org.dozer.loader.LoadMappingsResult;
import org.springframework.stereotype.Component;

import com.best.oasis.settlement.util.page.PageList;


public class OrikaBeanMapper {


	private List<String> mappingFiles;

	private final MapperFactory factory = new DefaultMapperFactory.Builder()
			.build();

	public List<String> getMappingFiles() {
		return mappingFiles;
	}

	public void setMappingFiles(List<String> mappingFiles) {
		this.mappingFiles = mappingFiles;
	}

	public void init() throws ClassNotFoundException, InstantiationException, IllegalAccessException {

	    LoadMappingsResult loadMappingsResult = null;
	    loadMappingsResult = loadMappings(loadMappingsResult);
	    if(loadMappingsResult ==null){
	    	loadMappingsResult = loadMappings2(loadMappingsResult);
	    }
	    ClassMappings customMappings = loadMappingsResult.getCustomMappings();
	    Configuration globalConfiguration = loadMappingsResult.getGlobalConfiguration();
	    registryConverters(globalConfiguration);
	    registryClassMappings(customMappings);

	}


	/*
	 * Load dozer mapping from configuration xml files.
	 * this works for dozer 5.3.0 version
	 */
	private LoadMappingsResult loadMappings2(
			LoadMappingsResult loadMappingsResult)
			throws IllegalAccessException {
		try {
		CustomMappingsLoader customMappingsLoader = new CustomMappingsLoader();
		Method loadMethod= CustomMappingsLoader.class.getMethod("load", List.class, List.class);
		List<MappingFileData> builderMappings = new ArrayList<MappingFileData>();
		loadMappingsResult = (LoadMappingsResult)  loadMethod.invoke(customMappingsLoader, mappingFiles, builderMappings);
		} catch (NoSuchMethodException e1) {

		} catch (SecurityException e1) {

		}catch (IllegalArgumentException e) {

		} catch (InvocationTargetException e) {
			
		}
		return loadMappingsResult;
	}

	/*
	 * Load dozer mapping from configuration xml files.
	 * this works for dozer 5.2.0 version
	 */
	private LoadMappingsResult loadMappings(
			LoadMappingsResult loadMappingsResult)
			throws IllegalAccessException {
		try {
	    	CustomMappingsLoader customMappingsLoader = new CustomMappingsLoader();
	    	 Method loadMethod = CustomMappingsLoader.class.getMethod("load", List.class);
	    	loadMappingsResult = (LoadMappingsResult) loadMethod.invoke(customMappingsLoader, mappingFiles);

		}catch (NoSuchMethodException e1) {

		}catch (SecurityException e1) {

		}catch (IllegalArgumentException e) {

		}catch (InvocationTargetException e) {

		}
		return loadMappingsResult;
	}

	private void registryClassMappings(ClassMappings customMappings) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		  Map<String, ClassMap> map = customMappings.getAll();

		    Iterator<String> iter =   map.keySet().iterator();
		    String key=null;
		    ClassMap dozerClassMap = null;
		    while(iter.hasNext()){
		    	key = iter.next();
		    	dozerClassMap = map.get(key);
		    	Class typeAClass = Class.forName(dozerClassMap.getSrcClassName());
		    	Class typeBClass = Class.forName(dozerClassMap.getDestClassName());
		    	ClassMapBuilder classMapBuilder = factory.classMap(typeAClass,
						typeBClass).byDefault();
		    	List<FieldMap> dozerFiledMaps = dozerClassMap.getFieldMaps();
		    	for (FieldMap dozerFiledMap: dozerFiledMaps){
		    		
		    		if(dozerFiledMap.getCustomConverterId()!=null && !"".equals(dozerFiledMap.getCustomConverterId())){
		    			//we use converter class name as converter id; 
		    			String converterId = dozerFiledMap.getCustomConverterId();
		    			
		    			if( !factory.getConverterFactory().hasConverter(converterId)){
		    				Class converterClaz = Class.forName(converterId);
		    				factory.getConverterFactory().registerConverter(converterId,  (ma.glasnost.orika.Converter) converterClaz.newInstance());
		    			}
		    			classMapBuilder
						.fieldMap(dozerFiledMap.getSrcFieldName(), dozerFiledMap.getDestFieldName())
						.converter(dozerFiledMap.getCustomConverterId()).aToB()
						.add();
		    		}else{
		    			classMapBuilder.fieldAToB(dozerFiledMap.getSrcFieldName(), dozerFiledMap.getDestFieldName());
		    		}
		    	}
		    	factory.registerClassMap(classMapBuilder);
		    }
		
	}

	private void registryConverters(Configuration globalConfiguration) throws InstantiationException, IllegalAccessException {
		if(globalConfiguration.getCustomConverters()!=null){
			Iterator<CustomConverterDescription> iter =  globalConfiguration.getCustomConverters().getConverters().iterator();
			CustomConverterDescription converterDescription = null;
			while(iter.hasNext()){
				converterDescription = iter.next();
				Class  converterClaz = converterDescription.getType();
				ma.glasnost.orika.Converter  orikaConverter =   (ma.glasnost.orika.Converter)converterClaz.newInstance();
				factory.getConverterFactory().registerConverter(orikaConverter);
			}
		}

	}

	public <V, P> P convert(V base, final Class<P> target) {

		if (base != null) {
			return (P) factory.getMapperFacade().map(base, target);

		} else {
			return null;
		}
	}

	public <V, P> List<P> convertList(List<V> baseList, Class<P> target) {

		if (baseList == null) {
			return null;
		} else {
			List<P> targetList = new ArrayList<P>();
			for (V vo : baseList) {

				targetList.add((P) convert(vo, target));
			}
			return targetList;
		}
	}
	
	@SuppressWarnings("unchecked")
    public <V, P> PageList<P> convertPageList(PageList<V> basePageList, Class<P> target){
        if (basePageList == null) {
            return null;
        } else {
            List<P> targetList = null;
            List<V> baseList = basePageList.getList();
            if(baseList != null){
                targetList = new ArrayList<P>();
                for (V vo : baseList) {
                    targetList.add((P) convert(vo, target));
                }
            }
            PageList<P> result = new PageList<P>();
            result = convert(basePageList, result.getClass());
            result.setList(targetList);
            return result;
        }
    }


}
